home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / visulztn / saoimage / saoimage.lha / disppsct.c < prev    next >
C/C++ Source or Header  |  1991-08-14  |  21KB  |  630 lines

  1. #ifndef lint
  2. static char SccsId[] = "%W%  %G%";
  3. #endif
  4.  
  5. /* Module:    disppsct.c (Display Postscript)
  6.  * Purpose:    Dump contents of the display window to a postscript printer.
  7.  * Subroutine:    screen_dump()            returns: void
  8.  * Copyright:    1989 NOAO & Smithsonian Astrophysical Observatory
  9.  *        You may do anything you like with this file except remove
  10.  *        this copyright.  The Smithsonian Astrophysical Observatory
  11.  *        makes no representations about the suitability of this
  12.  *        software for any purpose.  It is provided "as is" without
  13.  *        express or implied warranty.
  14.  * Origin:      Doug Tody, NOAO (written for IRAF's Imtool)
  15.  * Modified:    {0} Michael VanHilst    initial version        1 July 1989
  16.  *        {1} Jay Travisano (STScI)    VMS changes        10 Nov 1989
  17.  *              {2} MVH BSDonly strings.h compatability           19 Feb 1990
  18.  *        {n} <who> -- <does what> -- <when>
  19.  */
  20.  
  21. #ifndef SUN
  22. #include <sys/file.h>
  23. #endif
  24.  
  25. #include <stdio.h>        /* stderr, NULL, etc. */
  26.  
  27. #ifndef VMS
  28. #include <pwd.h>
  29. #ifdef SYSV
  30. #include <string.h>        /* strlen, strcat, strcpy, strrchr */
  31. #else
  32. #include <strings.h>        /* strlen, strcat, strcpy, rindex */
  33. #endif
  34. #else
  35. #include <string.h>        /* strlen, strcat, strcpy, strrchr */
  36. #endif
  37.  
  38. #include <time.h>
  39. #include <X11/Xlib.h>        /* X window stuff */
  40. #include <X11/Xutil.h>        /* X window manager stuff */
  41. #include "hfiles/define.h"    /* define SZ_FNAME */
  42. #include "hfiles/constant.h"    /* define codes */
  43. #include "hfiles/struct.h"    /* declare structure types */
  44. #include "hfiles/extern.h"    /* extern main parameter structures */
  45. #include "hfiles/region.h"    /* regdrawRec */
  46. #include "hfiles/scale.h"    /* SCALEOFF */
  47. extern struct windowRec desktop;
  48. extern struct regdrawRec rgdraw;
  49.  
  50. #ifndef PSCRIPT
  51. screen_dump ()
  52. {
  53.   (void)fprintf(stderr, "Compiled without PostScript support.\n");
  54.   return;
  55. }
  56. #else
  57.  
  58. #define    R_DISPOSE "lpr -Plw -r %s"    /* printer dispose command    */
  59. #define P_DISPOSE "lpr -P%s -r %s"    /* dispose to user's printer    */
  60. #define    SEGSIZE        16        /* output segment size (bytes)    */
  61. #define    SEGBITS        (SEGSIZE*8)
  62. #define    BITFLIP        0        /* bit-flip each byte for P.S.?    */
  63. #define    NGREY        256        /* max color table size        */
  64. #define MARGIN         36        /* 0.5 inch            */
  65. #define    PAGE_WIDTH    612        /* 8.5x11, 72pt/inch             */
  66. #define    PAGE_HEIGHT    792        /* 8.5x11, 72pt/inch             */
  67.  
  68. #define    RT_DATA        'a'        /* record type codes        */
  69. #define    RT_ZERO        'b'
  70. #define    RT_FULL        'c'
  71. #define    RT_BKG1        'd'
  72. #define    RT_BKG2        'e'
  73. #define    BKGPAT_1    "22"        /* atom for stipple pattern    */
  74. #define    BKGPAT_2    "88"        /* atom for stipple pattern    */
  75.  
  76. static    void bitmap_to_postscript();
  77. static    char *make_label();
  78. static  void black_border();
  79.  
  80. /* SCREENDUMP -- Make a hardcopy of the indicated region of the screen on a
  81.  * hardcopy device.  Currently only two output formats are supported, Sun
  82.  * rasterfile output, or Postscript output for devices such as the Apple laser
  83.  * writer.  Postscript output is the default; rasterfile output is enabled
  84.  * if the variable 'R_RASTERFILE' is defined in the environment, with the
  85.  * string value being an sprintf format string with one decimal integer
  86.  * argument, used to create the rasterfile filename (e.g., "frame.%d").
  87.  * The variable 'R_DISPOSE' may be defined to specify how the Postscript or
  88.  * Sun rasterfile is to be disposed of.  If 'R_DISPOSE' is not defined and
  89.  * rasterfile output is specified, no dispose command will be issued.  If the
  90.  * variable is not defined and Postscript output is specified, the default
  91.  * dispose command defined above will be executed.
  92.  *
  93.  */
  94. void screen_dump ( nobuttons )
  95.      int nobuttons;    /* i: omit-the-buttons from the output image */
  96. {
  97.   register int i;
  98.   int width, height, area;
  99.   unsigned char *obuf;
  100.   unsigned char *zero;
  101.  
  102.   static int index = 0;        /* count calls to produce unique file names */
  103.   char tempfile[80], dispose[80];
  104.   int depth=8;            /* only bytes top PS dither is supported */
  105.   double wscale, hscale;
  106.   double scale;
  107.   int color_levels;        /* number of color levels in hardare */
  108.   int status;
  109.   int fd;
  110.   char *str;
  111.   FILE *fp;
  112.   unsigned long draw_mask, draw_fore, draw_back;
  113.   unsigned long menu_fore, menu_back;
  114.   unsigned long incl_fore, incl_back;
  115.   unsigned long excl_fore, excl_back;
  116.   int filled;
  117.   unsigned long fore, back;
  118.   XImage *ximage;
  119.  
  120. #ifndef SUN
  121.   char *mktemp();
  122. #endif
  123.  
  124.   char *calloc_errchk(), *getenv();
  125.   void close_disk(), clear_margins(), map_buf_repzoom();
  126.   void map_buf_subzoom(), disp_dispbox(), disp_panbox();
  127.  
  128.   /* if you want to grab the server do it here */
  129.   /* suspend all screen interactions while processing */
  130.   /* get dimensions */
  131.   if( color.ncolors <= 1 ) {
  132.     /* strategy: clear image, draw cursors, copy cursors, */
  133.     width = dispbox.width;
  134.     height = dispbox.height;
  135.     area = width * height;
  136.     /* allocate a data buffer and the line-all-zeroes array */
  137.     if( ((obuf = (unsigned char *)
  138.       calloc_errchk(area, sizeof(char), (char *)NULL)) == 0) ||
  139.         ((zero = (unsigned char *)
  140.       calloc_errchk(height, sizeof(char), (char *)NULL)) == 0) ) {
  141.       (void)fprintf(stderr, "Print failure: cannot allocate data buffers\n");
  142.       return;
  143.     }
  144.     if( coord.bd.clip ) {
  145.       /* if image does not fill display buffer, do something about it */
  146.       clear_margins((unsigned char *)dispbox.image.data, &coord.bd,
  147.             &coord.disp, 1, color.hard.std_white);
  148.     }
  149.     if( coord.bd.block < -1 )
  150.       map_buf_repzoom(&coord, (unsigned char *)obuf,
  151.               buffer.shortbuf, buffer.scalemap + SCALEOFF);
  152.     else
  153.       map_buf_subzoom(&coord, (unsigned char *)obuf,
  154.               buffer.shortbuf, buffer.scalemap + SCALEOFF,
  155.               MAX(coord.bd.block, 1));
  156.     if( color.halftone.inverse == 0 ) {
  157.       /* non-inverted image has high values dark */
  158.       register unsigned char *ptr = obuf;
  159.       register unsigned char *obuf_end = obuf + area;
  160.       do {
  161.     *ptr = 255 - *ptr;
  162.       } while( ++ptr < obuf_end );
  163.     }
  164.     /* make the edge a black border */
  165.     black_border(obuf, width, height);
  166.   } else {
  167.     unsigned short *map;
  168.  
  169.     width = desktop.width;
  170.     height = desktop.height;
  171.     color_levels = DisplayCells(color.display, color.screen);
  172.     if( nobuttons )
  173.       height -= btnbox.height;
  174.     /* get byte image and convert to equivalent intensity */
  175.     /* allocate a val-to-intensity map and a this-line-all-zeroes array */
  176.     if( ((map = (unsigned short *)
  177.      calloc_errchk(NGREY, sizeof(short), "print map")) == 0) ||
  178.         ((zero = (unsigned char *)
  179.       calloc_errchk(height, sizeof(char), (char *)NULL)) == 0) ) {
  180.       (void)fprintf(stderr, "Print failure: cannot allocate data buffers\n");
  181.       return;
  182.     } 
  183.     
  184.     {
  185.       double rwght, gwght, bwght;
  186.       XColor cdef[256];
  187.       int val;
  188.  
  189.       /* Black&White = (.35*Red +.55*Green + .10*Blue) */
  190.       /* scaling from 65536 to 256 requires /256 */
  191.       rwght = 0.35 / 256.0;
  192.       gwght = 0.55 / 256.0;
  193.       bwght = 0.10 / 256.0;
  194.       for( i=0; i<color_levels; i++ )
  195.     cdef[i].pixel = i;
  196.       XQueryColors(color.display, color.colormap, cdef, color_levels);
  197.       for( i=0; i<color_levels; i++ ) {
  198.     val = (int)((rwght * (double)cdef[i].red) +
  199.             (gwght * (double)cdef[i].green) +
  200.             (bwght * (double)cdef[i].blue));
  201.     if( val >= 256 ) {
  202.       map[i] = 255;
  203.     } else if( val < 0 ) {
  204.       map[i] = 0;
  205.     } else {
  206.       map[i] = val;
  207.     }
  208.       }
  209.     }
  210.     /* set cursor colors (for max contrast with background) */
  211.     map[color.hard.std_black] = 0;
  212.     map[color.hard.std_white] = 255;
  213.     if( map[color.pixvalmap[0]] < 128 ) {
  214.       fore = color.hard.std_white;
  215.       back = color.hard.std_black;
  216.     } else {
  217.       fore = color.hard.std_black;
  218.       back = color.hard.std_white;
  219.     }
  220.     draw_mask = color.gcset.draw.mask;
  221.     draw_fore = color.gcset.draw.foreground;
  222.     draw_back = color.gcset.draw.background;
  223.     menu_fore = color.gcset.menu.foreground;
  224.     menu_back = color.gcset.menu.background;
  225.     incl_fore = color.gcset.incl.foreground;
  226.     incl_back = color.gcset.incl.background;
  227.     excl_fore = color.gcset.excl.foreground;
  228.     excl_back = color.gcset.excl.background;
  229.     filled = rgdraw.filled_label;
  230.     color.gcset.draw.mask = AllPlanes;
  231.     color.gcset.draw.foreground = fore;
  232.     color.gcset.draw.background = back;
  233.     color.gcset.menu.foreground = fore;
  234.     color.gcset.menu.background = back;
  235.     color.gcset.incl.foreground = fore;
  236.     color.gcset.incl.background = back;
  237.     color.gcset.excl.foreground = fore;
  238.     color.gcset.excl.background = back;
  239.     rgdraw.filled_label = 1;
  240.     /* redraw the display with this coloring */
  241.     XRaiseWindow(desktop.display, desktop.ID);
  242.     disp_dispbox();
  243.     disp_panbox();
  244.     XSync(desktop.display, 0);
  245.     /* read data direct from window (image starts at x/y + borderwidth) */
  246.     ximage = XGetImage(desktop.display, desktop.ID, 0, 0,
  247.                desktop.width, desktop.height, AllPlanes, ZPixmap);
  248.     color.gcset.draw.mask = draw_mask;
  249.     color.gcset.draw.foreground = draw_fore;
  250.     color.gcset.draw.background = draw_back;
  251.     color.gcset.menu.foreground = menu_fore;
  252.     color.gcset.menu.background = menu_back;
  253.     color.gcset.incl.foreground = incl_fore;
  254.     color.gcset.incl.background = incl_back;
  255.     color.gcset.excl.foreground = excl_fore;
  256.     color.gcset.excl.background = excl_back;
  257.     rgdraw.filled_label = filled;
  258.     disp_dispbox();
  259.     disp_panbox();
  260.     XSync(desktop.display, 0);
  261.     if( ximage == NULL ) {
  262.       (void)fprintf(stderr, "ERROR: could not read image\n");
  263.       return;
  264.     }
  265.     /* remap image values to monochrome intensities */
  266.     obuf = (unsigned char *)ximage->data;
  267.     {
  268.       register unsigned char *buf, *bufend, *ibuf;
  269.       buf = obuf;
  270.       if( nobuttons )
  271.     bufend = buf + (width * btnbox.y);
  272.       else
  273.     bufend = obuf + (width * desktop.height);
  274.       do {
  275.     *buf = map[*buf];
  276.       } while( ++buf < bufend );
  277.       if( nobuttons ) {
  278.     ibuf = obuf + (width * (dispbox.y - 1));
  279.     bufend = obuf + (width * desktop.height);
  280.     do {
  281.       *buf++ = map[*ibuf];
  282.     } while( ++ibuf < bufend );
  283.       }
  284.     }
  285.   }
  286.   /* make the edge a black border */
  287.     /*  black_border(obuf, width, height); */
  288.   /* Create a temporary file to hold postscript program. */
  289.   /* add unique char (mkstemp doesn't avoid duplicating temp file names) */
  290. #ifndef VMS
  291.   sprintf(tempfile, "/tmp/ps%cXXXXXX", 'a' + index);
  292. #else
  293.   sprintf(tempfile, "_ps%cXXXXXX.tmp", 'a' + index);
  294. #endif
  295.   if( ++index > 26 ) index = 0;
  296.   /* open a file with a system chosen unique name */
  297. #ifdef SUN
  298.   if( (fd = mkstemp(tempfile)) == -1 ) {
  299.     perror("Cannot make temp file");
  300. #else
  301.   (void)mktemp(tempfile);
  302.   /* open the file with a call to open with both write and create flags */
  303.   if( (fp = fopen(tempfile, "w")) == NULL ) {
  304. #endif
  305.     (void)fprintf(stderr, "cannot create temporary file %s\n", tempfile);
  306.     (void)fflush(stderr);
  307.   } else {
  308. #ifdef SUN
  309.     fp = fdopen(fd, "a");
  310. #endif
  311.     /* Autoscale to fit output page. */
  312.     wscale = ((double)PAGE_WIDTH - 2*MARGIN) / (double)width;
  313.     hscale = ((double)PAGE_HEIGHT - 2*MARGIN) / (double)height;
  314.     if( wscale < hscale )
  315.       scale = wscale;
  316.     else
  317.       scale = hscale;
  318.  
  319.     /* Translate the bitmap into a postscript program. */
  320.     bitmap_to_postscript(fp, obuf, width, height, depth, width, zero, scale);
  321.  
  322.     free((char *)zero);
  323.     (void)fclose(fp);
  324. #ifdef SUN
  325.     close_disk(fd, tempfile);
  326. #endif
  327.  
  328.     /* Dispose of tempfile to the printer.  We leave it up to the dispose
  329.      * command to delete the temporary file when finished.  The dispose
  330.      * command may be passed as an environment variable if desired, e.g.,
  331.      * to specify a printer device other than "lw" (laserwriter).
  332.      */
  333.     if( (str = getenv("R_DISPOSE")) != NULL ) {
  334.       sprintf(dispose, str, tempfile);
  335.     } else if( (str = getenv("PRINTER")) != NULL ) {
  336.       sprintf(dispose, P_DISPOSE, str, tempfile);
  337.     } else
  338.       sprintf(dispose, R_DISPOSE, tempfile);
  339. #ifndef VMS
  340.     if( (status = system(dispose)) != 0 )
  341. #else
  342.     if( !((status = system(dispose)) & 1) )
  343. #endif
  344.       (void)fprintf(stderr, "screendump: exit status %d\n", status);
  345.   }
  346.   if( color.ncolors <= 1 )
  347.     free((char *)obuf);
  348.   else
  349.     (void)XDestroyImage(ximage);
  350. }
  351.  
  352. /* BITMAP_TO_POSTSCRIPT -- Translate a memory bitmap into a postscript program
  353.  * using image compression where regions of the image are all zeroes.  This is
  354.  * done as follows: [1] lines of the bitmap are divided into segments of N
  355.  * bytes, [2] if all N bytes are zero a single zero byte is transmitted,
  356.  * otherwise a byte with the value one is transmitted, followed by N bytes of 
  357.  * literal data.  Lines which are entirely zero are not transmitted at all.
  358.  * The goal is to significantly reduce the amount of data to be pushed through
  359.  * the laserwriter serial interface while keeping things simple enough that
  360.  * postscript will hopefully be able to process the bitmap efficiently.
  361.  *
  362.  * NOTE: Postscript is supposed to be able to copy bitmaps directly without
  363.  * any transformations if all the right conditions are met, e.g., unitary
  364.  * matrices, pixrect resolution matches device resolution, etc.  We do not
  365.  * make use of this here due to the great volume of data which would have to
  366.  * pushed through the laserwriter serial interface at 9600 baud to transmit
  367.  * a fully resolved bitmap.  If a parallel interface were available, e.g.,
  368.  * if the laserwriter is on the ethernet, then this would be the way to go.
  369.  */
  370. static void
  371. bitmap_to_postscript (fp, bitmap, width, height, depth, linebytes, zero, scale)
  372.      register FILE *fp;
  373.      unsigned char *bitmap;
  374.      int width, height, depth;
  375.      int linebytes;
  376.      unsigned char *zero;
  377.      double scale;
  378. {
  379.   register unsigned char *ip;
  380.   register char    *op, *hp;
  381.   register int    n;
  382.   unsigned char    *segp;
  383.   char    hbuf[NGREY*2];
  384.   char    obuf[SEGSIZE*2];
  385.   char    rt_full[SEGSIZE*2+1];
  386.   char    bkg_1[SEGSIZE*2+1];
  387.   char    bkg_2[SEGSIZE*2+1];
  388.   int    partseg, seg, nsegs, allzeroes, i, j, last_j;
  389.   int    llx, lly, urx, ury;
  390.   long    clock;
  391.  
  392.   clock = time (0);
  393.  
  394.   /* Initialize the hbuf array, which contains the hex encoded
  395.    * representations of the NGREY possible binary byte values.
  396.    */
  397.   for (n=0, op=hbuf;  n < NGREY;  n++) {
  398.     i = ((n >> 4) & 017);
  399.     *op++ = (i < 10) ? i + '0' : (i-10) + 'A';
  400.     i = (n & 017);
  401.     *op++ = (i < 10) ? i + '0' : (i-10) + 'A';
  402.   }
  403.   
  404.   /* Set up the background (stipple) pattern arrays, used to represent
  405.    * the Sunview background pattern outside of windows.
  406.    */
  407.   for (op=bkg_1, hp=BKGPAT_1, n=SEGSIZE;  --n >= 0;  ) {
  408.     *op++ = hp[0];
  409.     *op++ = hp[1];
  410.   }   *op++ = '\0';
  411.   for (op=bkg_2, hp=BKGPAT_2, n=SEGSIZE;  --n >= 0;  ) {
  412.     *op++ = hp[0];
  413.     *op++ = hp[1];
  414.   }   *op++ = '\0';
  415.   
  416.   /* RT_FULL is a solid line, another common pattern. */
  417.   for (op=rt_full, n=SEGSIZE*2;  --n >= 0;  )
  418.     *op++ = 'F';
  419.   *op++ = '\0';
  420.   
  421.   /* Initialize obuf, in case a partseg call causes the full buffer to
  422.    * be written out before the garbage elements at the end have been
  423.    * initialized to legal values.
  424.    */
  425.   bcopy ((char *)rt_full, (char *)obuf, SEGSIZE*2);
  426.  
  427.   /* Calculate bounding box. */
  428.   llx = MARGIN;
  429.   ury = PAGE_HEIGHT - MARGIN;
  430.   urx = llx + (width * scale);
  431.   lly = ury - (height * scale);
  432.  
  433.   /* Define the postscript necessary to receive and output the lines
  434.    * of the pixrect with image compression.
  435.    */
  436.   (void)fprintf(fp, "%%!PS-Adobe-2.0 EPSF-2.0\n");
  437.   (void)fprintf(fp, "%%%%Title: SAOimage screendump\n");
  438.   (void)fprintf(fp, "%%%%Creator: SAOimage\n");
  439.   (void)fprintf(fp, "%%%%CreationDate: %s", asctime(localtime(&clock)));
  440.   (void)fprintf(fp, "%%%%PageOrientation: Portrait\n");
  441.   (void)fprintf(fp, "%%%%Pages: 1\n");
  442.   (void)fprintf(fp, "%%%%BoundingBox: %d %d %d %d\n", llx, MARGIN/2, urx, ury);
  443.   (void)fprintf(fp, "%%%%DocumentFonts: Times-Roman\n");
  444.   (void)fprintf(fp, "%%%%DocumentNeededFonts: Times-Roman\n");
  445.   (void)fprintf(fp, "%%%%EndComments\n");
  446.   (void)fprintf(fp, "/r_data %d string def\n", SEGSIZE);
  447.   (void)fprintf(fp, "/r_zero %d string def\n", SEGSIZE);
  448.   (void)fprintf(fp, "/r_full %d string def\n", SEGSIZE);
  449.   (void)fprintf(fp, "/r_bkg1 %d string def\n", SEGSIZE);
  450.   (void)fprintf(fp, "/r_bkg2 %d string def\n", SEGSIZE);
  451.   (void)fprintf(fp, "currentfile r_full readhexstring %s\n", rt_full);
  452.   (void)fprintf(fp, "currentfile r_bkg1 readhexstring %s\n", bkg_1);
  453.   (void)fprintf(fp, "currentfile r_bkg2 readhexstring %s\n", bkg_2);
  454.   (void)fprintf(fp, "clear\n");
  455.  
  456.   (void)fprintf(fp, "/dline {\n");
  457.  
  458.   if (depth == 8) {
  459.     (void)fprintf(fp, " %d div neg 0 exch translate\n", height);
  460.     (void)fprintf(fp, " %d %d 8\n", width, 1);
  461.     (void)fprintf(fp, " [ %d 0 0 %d 0 %d ]\n", width, -height, height);
  462.   } else {
  463.     (void)fprintf(fp," 0 exch translate %d %d true matrix\n", width, 1);
  464.   }
  465.  
  466.   (void)fprintf(fp, " { currentfile read pop dup %d eq\n", RT_DATA);
  467.   (void)fprintf(fp, "     { pop currentfile r_data readhexstring pop }\n");
  468.   (void)fprintf(fp, "     { dup %d eq\n", RT_ZERO);
  469.   (void)fprintf(fp, "       { pop r_zero }\n");
  470.   (void)fprintf(fp, "       { dup %d eq\n", RT_FULL);
  471.   (void)fprintf(fp, "         { pop r_full }\n");
  472.   (void)fprintf(fp, "         { %d eq\n", RT_BKG1);
  473.   (void)fprintf(fp, "           { r_bkg1 }\n");
  474.   (void)fprintf(fp, "           { r_bkg2 }\n");
  475.   (void)fprintf(fp, "           ifelse }\n");
  476.   (void)fprintf(fp, "         ifelse }\n");
  477.   (void)fprintf(fp, "       ifelse }\n");
  478.   (void)fprintf(fp, "     ifelse\n");
  479.  
  480.   if (depth == 8)
  481.     (void)fprintf(fp, " } image} def\n");
  482.   else
  483.     (void)fprintf(fp, " } imagemask} def\n");
  484.  
  485.   (void)fprintf(fp, "%%%%EndProlog\n");
  486.   (void)fprintf(fp, "%%%%Page: 1 1\n");
  487.  
  488.   (void)fprintf(fp, "gsave\n");
  489.   (void)fprintf(fp, "%d %d translate\n", llx, lly);
  490.   (void)fprintf(fp, "%d %d scale\n", urx-llx, ury-lly);
  491.  
  492.   nsegs = width / (SEGBITS / depth);
  493.   partseg = linebytes - (nsegs * SEGSIZE);
  494.  
  495.   /* Output successive lines of the pixrect.  All zero lines are omitted
  496.    * and data compression is used for large regions of zeroes embedded
  497.    * within a line.
  498.    */
  499.   for (j=0, last_j=0;  j < height;  j++) {
  500.     if (zero[j])
  501.       continue;
  502.  
  503.     (void)fprintf(fp, "\n%d dline\n", j - last_j);
  504.     last_j = j;
  505.  
  506.     /* Output an integral number of line segments in hexstring format,
  507.      * i.e., two hex digits output per binary input byte.
  508.      */
  509.     segp = bitmap + j*linebytes;
  510.     for (seg=0;  seg < nsegs;  seg++, segp += SEGSIZE) {
  511.       /* Quick scan of the data to see if it is all zeroes. */
  512.       allzeroes = 1;
  513.       for (ip=segp, n=SEGSIZE;  --n >= 0;  )
  514.     if (*ip++) {
  515.       allzeroes = 0;
  516.       break;
  517.     }
  518.       
  519.       if (allzeroes) {
  520.     (void)putc(RT_ZERO, fp);
  521.       } else {
  522.     /* Encode the data segment in hex format. */
  523.     for( ip=segp, op=obuf, n=SEGSIZE;  --n >= 0;  ) {
  524.       hp = hbuf + (*ip++ * 2);
  525.       *op++ = *hp++;
  526.       *op++ = *hp++;
  527.     }
  528.     
  529.     if( (obuf[0] == rt_full[0]) &&
  530.         (strncmp(obuf, rt_full, SEGSIZE*2) == 0) ) {
  531.       (void)putc(RT_FULL, fp);
  532.     } else if( (obuf[0] == bkg_1[0]) &&
  533.            (strncmp(obuf, bkg_1, SEGSIZE*2) == 0) ) {
  534.       (void)putc (RT_BKG1, fp);
  535.     } else if( (obuf[0] == bkg_2[0]) &&
  536.            (strncmp(obuf, bkg_2, SEGSIZE*2) == 0) ) {
  537.       (void)putc(RT_BKG2, fp);
  538.     } else {
  539.       (void)putc(RT_DATA, fp);
  540.       (void)fwrite(obuf, SEGSIZE*2, 1, fp);
  541.     }
  542.       }
  543.     }
  544.  
  545.     /* Write out any partial segment at the end of the line.  We must
  546.      * always write a full segment, even if the data at the end is
  547.      * garbage, else synchronization will be lost.
  548.      */
  549.     if (partseg) {
  550.       for( op=obuf, n=partseg;  --n >= 0;  ) {
  551.     hp = hbuf + (*ip++ * 2);
  552.     *op++ = *hp++;
  553.     *op++ = *hp++;
  554.       }
  555.       (void)putc(RT_DATA, fp);
  556.       (void)fwrite(obuf, SEGSIZE*2, 1, fp);
  557.     }
  558.   }
  559.  
  560.   /* Add the [SAO] logo and timestamp at the bottom of the page and
  561.    * output the page.
  562.    */
  563.   (void)fprintf(fp, "\ngrestore\n");
  564.   (void)fprintf(fp, "/Times-Roman findfont 6 scalefont setfont\n");
  565.   if( img.filename != NULL ) {
  566.     (void)fprintf(fp, "%d %d moveto\n", 2*MARGIN, MARGIN/2);
  567.     (void)fprintf(fp, "(%s) show\n", img.filename);
  568.   }
  569.   (void)fprintf(fp, "%d %d moveto\n", width - 144, MARGIN/2);
  570.   (void)fprintf(fp, "(%s) show\n", make_label());
  571.   (void)fprintf(fp, "showpage\n");
  572.   (void)fprintf(fp, "%%%%EOF\n");
  573. }
  574.  
  575. /*
  576.  * Subroutine:    make_label
  577.  * Purpose:    Generate the label for the output printer page.
  578.  * Unix call:    time(), gethostname(), getpwuid(), endpwent()
  579.  */
  580. static char *make_label()
  581. {
  582.   static char buf[128];
  583.   char hostname[32];
  584.   char username[32];
  585. #ifndef VMS
  586.   struct passwd *pw;
  587.   long time();        /* should be in time.h, but only VMS does it */
  588. #else
  589.   char *getenv();
  590. #endif
  591.   long clock;
  592.   int gethostname();
  593.  
  594.   clock = time((long *)0);
  595.   (void)gethostname(hostname, 32);
  596. #ifndef VMS
  597.   pw = getpwuid(getuid());
  598.   (void)strcpy(username, pw->pw_name);
  599.   (void)endpwent();
  600. #else
  601.   (void)strcpy(username,getenv("USER"));
  602. #endif
  603.  
  604.   sprintf(buf, "SAOimage  %s@%s  %s",
  605.         username, hostname, asctime(localtime(&clock)));
  606.   return(buf);
  607. }
  608.  
  609. static void black_border ( obuf, width, height )
  610.      unsigned char *obuf;
  611.      int width, height;
  612. {
  613.   register unsigned char *buf, *bufend;
  614.   buf = obuf;
  615.   bufend = buf + (width * height);
  616.   while( (buf+=width) < bufend ) {
  617.     *buf = 0;
  618.     *(buf-1) = 0;
  619.   }
  620.   buf = bufend - width;
  621.   while( ++buf < bufend )
  622.     *buf = 0;
  623.   buf = obuf;
  624.   bufend = buf + width;
  625.   while( buf < bufend )
  626.     *buf++ = 0;
  627. }
  628.  
  629. #endif
  630.